package control;

import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Vector;
import service.ChatClientService;
import dto.Account;
import dto.Message;
import functions.ServerMails;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import service.*;
//import control.ChatRoomImp;

public class ChatServerServiceImpl implements service.ChatServerService {

    private ServerController controller;
    private HashMap<Account, ChatClientService> clients = new HashMap<>();
    private static List<SessionInt> sessions = new ArrayList<>();
    private Thread announcementThread;
    private String[] announcements = {"Now, You can Play Online with www.el3ab.com", "Buy your Books online from www.bookStore.com", "Chat more with the new Version of BIMW Messenger!", "See the most interesting places to visit with www.toursimmania.com", "Use the new feature of emotions with BIMW messenger"};
    int advCounter, numOfannouncement = 5, update = 0;

    protected ChatServerServiceImpl(ServerController controller) throws RemoteException {

        //announcements= new String[10];
        /**
         * Preparing the announcements' thread that server used to 
         * send Advertisements to Clients.
         */
        advCounter = 0;
        this.controller = controller;
        announcementThread = new Thread(new Runnable() {
            @Override
            public void run() {
                //String[] adv = {"Adv 1", "Adv 2"};
                while (true) {
                    Object[] clientsRef = clients.values().toArray();
                    if (clientsRef != null) {
                        for (int i = 0; i < clientsRef.length; i++) {
                            if (advCounter == numOfannouncement) {
                                advCounter = 0;
                            }

                            ChatClientService clientsRefCast = (ChatClientService) clientsRef[i];
                            try {
                                clientsRefCast.announce(announcements[advCounter++]);
                            } catch (RemoteException ex) {
                                ex.printStackTrace();
                            }
                        }
                        try {
                            announcementThread.sleep(3500);
                        } catch (InterruptedException ex) {
                            ex.printStackTrace();
                        }
                    }
                }
            }
        });
        announcementThread.start();
    }

    @Override
    public boolean signUp(Account a) throws RemoteException {
        if(controller.signUp(a)) {
            controller.updateSignUpAdminView();
            return true;
        }
        return false;
    }

    @Override
    public SessionInt signIn(ChatClientService a) throws RemoteException, Exception {
        Account account = a.getLoggedInAccount();
        controller.signIn(account);
        if(!clients.containsKey(account)) {
            controller.updateLoginAdminView();
            clients.put(account, a);
        } else {
            throw new Exception("loggedInAccount");
        }
        SessionImp session = new SessionImp(account.getEmail());
        session.setServerController(controller);
        session.setName(controller.getAccountByMail(account.getEmail()).getFirstName());
        return session;
    }

    @Override
    public void setStatus(String status, ChatClientService client) throws RemoteException {
        Account changed = client.getLoggedInAccount();
        changed.setStatus(status);
        if (changed.getContactList() != null) {
            for (Iterator it = changed.getContactList(); it.hasNext();) {
                ChatClientService c = clients.get(it.next());
                if (c != null) {
                    c.notifyState(changed);
                }
            }
        }
    }

    @Override
    public void sendMessage(Message message, ChatRoomInt cs) throws RemoteException {
        for (Account member : cs.getChatRoomMembers()) {
            ChatClientService client = clients.get(member);
            if (client != null) {
                client.reciveMessage(message, cs);
            } else {
                if(message.getBody() != null)
                    controller.saveMessage(message, member);
            }
        }
    }
    
    @Override
    public void sendBuzz(ChatRoomInt cs) throws RemoteException {

        for (Account member : cs.getChatRoomMembers()) {
            ChatClientService client = clients.get(member);
            if (client != null) {
                client.reciveBuzz(cs);
            }

        }

    }
    @Override
    public void removeMember(Account accountToRemove, ChatRoomInt memberRoom) throws RemoteException {
        Vector<Account> roomMembers = memberRoom.getChatRoomMembers();
        roomMembers.removeElement(accountToRemove);
        memberRoom.setChatRoomMembers(roomMembers);
        for (Account member : roomMembers) {
            ChatClientService client = clients.get(member);
            if(client != null)
                client.updateMembersList(accountToRemove,memberRoom);
        }
    }
    
    @Override
    public void addToMemberList(Account account,ChatRoomInt membersRoom) throws RemoteException{
    membersRoom.getChatRoomMembers().add(account);
    for(Account acc:membersRoom.getChatRoomMembers()){
        
        ChatClientService client=clients.get(acc);
        
        client.addToMemberList(account, membersRoom);
    
    }
    
    }




    /**
     * to send Confirmation mail After registration by Server.
     */
    @Override
    public Boolean sendMail(Account sender) throws RemoteException {

        ServerMails mails = new ServerMails();
        return mails.sendMail(sender);
    }

    /**
     * to send mail from client by Server.
     */
    @Override
    public Boolean sendMail(String sender, String reciever, String title, String message) {
        ServerMails mails = new ServerMails();
        return mails.sendMail(sender, reciever, title, message);

    }

    /**
     * method to add contact to the client contact List.
     * the method checks if the new contact is online, then send him/her the request
     * if the new contact is offline, method will store the request in DB till the contact will be online.
     */
    @Override
    public void addContact(String mail, Account senderAccount) throws RemoteException {

        Account addedContact = controller.getAccountByMail(mail);
        if (addedContact != null) {
            ChatClientService client = getOnlineclientService(addedContact);

            if (client != null) // user is online
            {
                client.friendRequest(senderAccount);

            } else // user is offline
            {
                // save details in PinnedFR
                String sendermail = senderAccount.getEmail();
                String receiver = mail;

                controller.storeFriendrequest(sendermail, receiver);

            }

        }
        //return controller.addContact(mail, senderClient.getLoggedInAccount());
    }

    /**
     * method to get the contact list of each user from DB.
     */
    @Override
    public Vector<Account> getContactList(ChatClientService myAccount) throws RemoteException {
        Vector<Account> accounts = controller.getContactList(myAccount.getLoggedInAccount());
        if (accounts != null) {
            for (int i = 0; i < accounts.size(); i++) {
                ChatClientService client = getOnlineclientService(accounts.get(i));
                if (client != null && !myAccount.getLoggedInAccount().getStatus().equalsIgnoreCase("OFFLINE")) {
                    accounts.get(i).setStatus(client.getLoggedInAccount().getStatus());
                    client.notifyState(myAccount.getLoggedInAccount());
                }
            }
        }
        return accounts;
    }

    ChatClientService getOnlineclientService(Account checkedAccount) {
        Account account;
        ChatClientService ClientServiceref = null;
        Object[] clientsAccountSet = clients.keySet().toArray();
        for (int i = 0; i < clientsAccountSet.length; i++) {
            account = (Account) clientsAccountSet[i];
            if (account.getEmail().equalsIgnoreCase(checkedAccount.getEmail())) {
                ClientServiceref = clients.get(account);
                break;
            }
        }
        return ClientServiceref;
    }

    /**
     * method to remove contact from user's contact list.
     */
    @Override
    public Boolean removeContact(String mail, ChatClientService account) throws RemoteException {
        return controller.removeContact(mail, account.getLoggedInAccount());
    }

    @Override
    public void logout(Account logedOutAccount) throws RemoteException {
        Account account;
        Object[] clientsAccountSet = clients.keySet().toArray();
        for (int i = 0; i < clientsAccountSet.length; i++) {
            account = (Account) clientsAccountSet[i];
            if (account.getEmail().equalsIgnoreCase(logedOutAccount.getEmail())) {
                account.setStatus("OFFLINE");
                clients.get(account).notifyState(account);
                if(!(logedOutAccount.getStatus().equals("OFFLINE") && clients.get(logedOutAccount).getLoggedInAccount().getStatus().equals("OFFLINE")))
                    setStatus("OFFLINE", clients.get(logedOutAccount));
                clients.remove(account);
                break;
            }
        }
//        if(clients.get(logedOutAccount).getLoggedInAccount().equals(logedOutAccount)) {
//            if(!(logedOutAccount.getStatus().equals("OFFLINE") && clients.get(logedOutAccount).getLoggedInAccount().getStatus().equals("OFFLINE")))
//                setStatus("OFFLINE", clients.get(logedOutAccount));
//            clients.remove(logedOutAccount);
//        }
    }
    /**
     * method to send the friend request Reply.
     * method checks if client is Online, then sends reply.
     * if user is offline, store reply in DB till user is online.
     */

    @Override
    public void friendRequestReply(Account userWhoAdd, Account userWhoReply, int reply) {

        if (reply == 0) {
            controller.addContact(userWhoAdd, userWhoReply);
        }

        ChatClientService userWhoAddService = getOnlineclientService(userWhoAdd);

        if (userWhoAddService != null) // userWhoAdd is online
        {
            try {
                userWhoAddService.friendRequestReply(userWhoReply, reply);
            } catch (RemoteException ex) {
                ex.printStackTrace();
            }
        } else // userWhoAdd is offline
        {
            //Store it in DB & Show it for request sender when login.
            String userWhoAddmail = userWhoAdd.getEmail();
            String userWhoReplymail = userWhoReply.getEmail();

            controller.storeFriendrequestReply(userWhoAddmail, userWhoReplymail, reply);


        }
    }

    /**
     * Checks if there 're Pinned Friend Requests 
     */
    @Override
    public void checkPinnedFriendRequest(ChatClientService clientService) {

        controller.checkPinnedFriendRequest(clientService);
    }
    /**
     * Checks if there 're Pinned Friend Request reply.
     */
    @Override
    public void checkPinnedFriendRequestReply(ChatClientService clientService) {

        controller.checkPinnedFriendRequestReply(clientService);
    }

    @Override
    public boolean checkExistingAccout(Account account) throws RemoteException {
        return controller.checkExistingAccout(account);
    }

    /**
     * @return the sessions
     */
    public static List<SessionInt> getSessions() {
        return sessions;
    }

    /**
     * @param aSessions the sessions to set
     */
    public static void setSessions(List<SessionInt> aSessions) {
        sessions = aSessions;
    }

    /**
     * @return the clients
     */
    public HashMap<Account, ChatClientService> getClients() {
        return clients;
    }

    /**
     * @param clients the clients to set
     */
//    public void setClients(HashMap<Account, ChatClientService> clients) {;
//        this.clients = clients;
//    }
    @Override
    public Vector<Message> getOfflineMessage(ChatClientService client) throws RemoteException {
        return controller.getOfflineMessage(client.getLoggedInAccount());
    }

    @Override
    public void upload_file(String upfileName,byte[] FileInputByte, Message msg, ChatRoomInt cs) {
        try {

            File dir = new File(System.getenv("USERPROFILE") + ("\\My Documents\\uploadedfiles"));
            if (dir.exists()) {
                System.out.println("Floder exists");
            } else {
                dir.mkdir();
            }

            File targetFile = new File(System.getenv("USERPROFILE") + "\\My Documents\\uploadedfiles\\" +upfileName);
            if (targetFile.exists()) {
                System.out.println("Target file is already exists");
                //System.exit(0);
            } else {
                 BufferedOutputStream output=null;
                try{
                    
                 output = new BufferedOutputStream(new FileOutputStream(targetFile));
                //BufferedInputStream input = new BufferedInputStream(new FileInputStream(sourceFile));
                //BufferedInputStream input = new BufferedInputStream(FIS); 
//                int r;
//                int numberOfBytesCopied = 0;
//                while ((r = input.read()) != -1) {
//                    output.write((byte) r);
//                    numberOfBytesCopied++;
//                }
               // input.close();
               output.write(FileInputByte);
                 }finally {
                            if(output != null) {
                           try  {
                                  //flush and close the BufferedOutputStream
                                    output.flush();
                                    output.close();
                                 }
                           catch(Exception e){}
      }
    }
              
                // Display the file size
                System.out.println(FileInputByte.length+ " bytes copied");
            }
        } catch (IOException ex) {
            Logger.getLogger(ChatServerServiceImpl.class.getName()).log(Level.SEVERE, null, ex);
        }
        try {
            msg.setBody(upfileName);
            for (Account member : cs.getChatRoomMembers()) {
                ChatClientService client = clients.get(member);
                if (client != null) {
                    client.receive_file_imfo(msg, cs);
                }
            }
        } catch (RemoteException ex) {
            Logger.getLogger(ChatServerServiceImpl.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    @Override
   public byte[] download_file(String fileName) //to download file when user request this by
    {  
        
        File fromserver = new File(System.getenv("USERPROFILE") + "\\My Documents\\uploadedfiles\\" + fileName);
        byte[] serverFile=new byte[(int)fromserver.length()];
        try {
            //  return fromserver;
             serverFile=readBytesFromFile(fromserver);
        } catch (IOException ex) {
            Logger.getLogger(ChatServerServiceImpl.class.getName()).log(Level.SEVERE, null, ex);
        }
        return serverFile;
    }
    private byte[] readBytesFromFile(File file) throws IOException {
      InputStream is = new FileInputStream(file);
      
      // Get the size of the file
      long length = file.length();
  
      // You cannot create an array using a long type.
      // It needs to be an int type.
      // Before converting to an int type, check
      // to ensure that file is not larger than Integer.MAX_VALUE.
      if (length > Integer.MAX_VALUE) {
        throw new IOException("Could not completely read file " + file.getName() + " as it is too long (" + length + " bytes, max supported " + Integer.MAX_VALUE + ")");
      }
  
      // Create the byte array to hold the data
      byte[] bytes = new byte[(int)length];
  
      // Read in the bytes
      int offset = 0;
      int numRead = 0;
      while (offset < bytes.length && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
          offset += numRead;
      }
  
      // Ensure all the bytes have been read in
      if (offset < bytes.length) {
          throw new IOException("Could not completely read file " + file.getName());
      }
  
      // Close the input stream and return bytes
      is.close();
      return bytes;
  }

    @Override
    public String[] getAnnouncement() {
        return announcements;
    }
    /**
     * Add new Announcemetns
     */
    public void addAnnouncements(String announcement) {
        if (update >= 5) {
            update = 0;
        }

        announcements[update++] = new String(announcement);
        if (numOfannouncement < 5) {
            numOfannouncement++;
        }
    }
}
